package wangjg.dbfx;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import javax.sql.rowset.CachedRowSet;

import com.sun.rowset.CachedRowSetImpl;

import wangjg.dbfx.dialect.SQLPaging;
import wangjg.dbfx.mapping.ResultMapping;
import wangjg.dbfx.mapping.SqlMapping;
import wangjg.dbfx.mapping.EntityInfo;
import wangjg.dbfx.mapping.EntityInfoManager;

/**
 * data operation
 * @author wangjg
 */
public class JdbcSession{
	private Connection conn;
	private SQLPaging paging;
	private EntityInfoManager ef;
	private SqlMapping sqlMapping;
	private ResultMapping rsMapping;
	
	public JdbcSession() {
		this.ef = new EntityInfoManager();
		this.sqlMapping = new SqlMapping(ef);
		this.rsMapping = new ResultMapping(ef);
	}

	public Serializable save(Object entity){
		try {
			PreparedStatement stmt = sqlMapping.getInsertStatement(getConnection(), entity);
			stmt.executeUpdate();
			ResultSet rs = stmt.getGeneratedKeys();  
			Serializable id = null;  
			if (rs!=null && rs.next()) {  
			    id = (Serializable) rs.getObject(1);
			}  
			return id;
		} catch (SQLException e) {
			throw new JdbcException(e);
		}
	}
	
	public int update(Object entity) {
		try {
			PreparedStatement stmt = sqlMapping.getUpdateStatement(getConnection(),entity);
			return stmt.executeUpdate();
		} catch (SQLException e) {
			throw new JdbcException(e);
		}
	}
	
	public int delete(Object entity){
		try {
			PreparedStatement stmt = sqlMapping.getDeleteStatement(getConnection(), entity);
			return stmt.executeUpdate();
		} catch (SQLException e) {
			throw new JdbcException(e);
		}
	}
	
	public int delete(Class<?> clazz, Object id){
		try {
			PreparedStatement stmt = sqlMapping.getDeleteStatement(getConnection(), clazz, id);
			return stmt.executeUpdate();
		} catch (SQLException e) {
			throw new JdbcException(e);
		}
	}

	public <T> List<T> query(Class<T> clazz, final String sql, final Object... values){
		return this.query(null, null, clazz, sql, values);
	}
	
	@SuppressWarnings("unchecked")
	public <T> List<T> query(Integer firstResult, Integer maxResults, Class<T> clazz, final String sql, final Object... values){
		try {
			String pagesql = sql;
			if (firstResult != null && maxResults != null) {
				pagesql = paging.getLimitString(sql, firstResult, maxResults);
			}
			ResultSet rs = this.executeQuery(pagesql, values);
			return rsMapping.toBeanList(rs, clazz);
		} catch (SQLException e) {
			throw new JdbcException(e);
		}
	}

	@SuppressWarnings("unchecked")
	public <T> T get(Class<T> clazz, Object id){
		try {
			EntityInfo ei = getEntityInfo(clazz);
			String sql="select * from "+ei.getTable()+" where "+ei.getId().getFieldName()+"=?";
			PreparedStatement stmt = getConnection().prepareStatement(sql);
			stmt.setObject(1, id);
			ResultSet rs = stmt.executeQuery();
			
			return (T) rsMapping.toBean(rs, clazz);
		} catch (SQLException e) {
			throw new JdbcException(e);
		}
	}

	private EntityInfo getEntityInfo(Class<?> c){
		return ef.getEntityInfo(c);
	}

	int _____sql_____;

	//jdbc
	public CachedRowSet executeQuery(final String sql, final Object... values){
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		try {
			pstmt = getConnection().prepareStatement(sql);
			for (int i = 0; i < values.length; i++) {
				pstmt.setObject(i+1, values[i]);
			}
			rs = pstmt.executeQuery();
			CachedRowSet crs = new CachedRowSetImpl();
			crs.populate(rs);
			return crs;
		} catch (SQLException e) {
			throw new JdbcException(e);
		}finally{
			if(pstmt!=null){
				try {
					pstmt.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
			if(rs!=null){
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public int executeUpdate(final String sql, final Object... values){
		try {
			PreparedStatement pstmt = getConnection().prepareStatement(sql);
			for(int i=1;i<=values.length;i++){
				pstmt.setObject(i, values[i]);
			}
			int n = pstmt.executeUpdate();
			return n;
		} catch (SQLException e) {
			throw new JdbcException(e);
		}
	}
	
	public void begin() throws SQLException{
		getConnection().setAutoCommit(false);
	}

	public void commit() throws SQLException {
		getConnection().rollback();
	}

	public void rollback() throws SQLException {
		getConnection().rollback();
	}
	
	protected void finalize() throws Throwable {
		System.out.println(this.getClass().getName()+" finalize");
	}

	Connection getConnection() throws SQLException {
//		System.out.println(conn.getMetaData().getDatabaseProductName());
		return conn;
	}
	
	void setConnection(Connection conn) throws SQLException {
		this.conn = conn;
	}

	void setPaging(SQLPaging paging) {
		this.paging = paging;
	}
}
